#!perl -w
use strict;
print "\n";

### Input data

my @log = (
  {user=>'Foo',  action=>'login',  'time'=>time - 60*60/6*6},
  {user=>'Marn', action=>'login',  'time'=>time - 60*60/6*4},
  {user=>'Foo',  action=>'logout', 'time'=>time - 60*60/6*3},
  {user=>'Marn', action=>'logout', 'time'=>time - 60*60/6*1},
  {user=>'Marn', action=>'login',  'time'=>time - 60*60/6*1 + 1},
  {user=>'Marn', action=>'logout', 'time'=>time - 60*60/6*1 + 2},
);

my @users = sort qw(Marn Foo);
my $available_width = 79;
my $t24hour = 0;

### setup

my $t_start = $log[0]{'time'};
my $t_range = time() - $t_start;

my $wid_user = 0;
for (map length, @users) {$wid_user = $_ if $_ > $wid_user}

my $wid_chart = $available_width - $wid_user - 3;
my $fmt = "%${wid_user}s >%${wid_chart}s>\n";

my $show_minutes = $t_range < 3*60*60;

### compute data

my $timeline = ' ' x $wid_chart;
my $prevstr = '';

my %state = map +($_, '-'), @users;
my %chart = map +($_, ''),  @users;

for (my $char = 0; $char < $wid_chart; $char++) {
  my $time = $t_start + $char * ($t_range / $wid_chart);

  # put time in title line
  {
    (undef, my $min, my $hour) = localtime($time);

    my $min_str = $show_minutes ? sprintf(":%02u", $min) : '';

    my $time_str = $t24hour
      ? $hour . $min_str
      : ($hour % 12 || 12) . $min_str . (int($hour / 12) ? 'p' : 'a');

    substr($timeline, $char, length $time_str) = $prevstr = $time_str
      if $time_str ne $prevstr and substr($timeline, $char-1, 1) eq ' '; 
  }

  my %changed;

  # change state character
  while ($log[0] and $log[0]{'time'} < $time) {
    my $record = shift @log;
    $state{$record->{user}} = $record->{action} eq 'login' ? '#' : '-';
    $changed{$record->{user}}++;
  }

  # add state character to line
  for (@users) {
    $chart{$_} .= ($changed{$_} || 0) > 1 ? '*' : $state{$_};
  }
}

print "Key:  - absent  # present  * both\n"
      . sprintf($fmt, '', substr($timeline, 0, $wid_chart))
      . join('', map sprintf($fmt, $_, $chart{$_}), @users);
